﻿#ifndef TEMPLATEMATCH_H
#define TEMPLATEMATCH_H

#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>


/**
 * @brief match 模板匹配，不考虑图像旋转
 * @param image
 * @param tepl 模板图像
 * @param point 返回匹配的左上角坐标
 * @param method 匹配算法。
 *      CV_TM_SQDIFF 差值平方和匹配, 最好匹配为 0。
 *      CV_TM_SQDIFF_NORMED 标准化差值平方和匹配, 最好匹配为 0。
 *      CV_TM_CCORR 相关匹配，越大表示匹配越好。
 *      CV_TM_CCORR_NORMED 标准相关匹配，越大表示匹配越好。
 *      CV_TM_CCOEFF_NORMED 相关匹配，越大表示匹配越好。
 *      CV_TM_CCOEFF 标准相关匹配，越大表示匹配越好，1 表示完全相同。
 * @return
 */
double match(const cv::Mat &image, const cv::Mat &tepl, cv::Point &point, int method);

double match0(const cv::Mat &image, const cv::Mat &tepl, cv::Point &point, int method); // 没有针对模板大小做归一化

/**
 * @brief rotate 图像旋转，不改变图像的大小。以图像的中心点为旋转中心。
 * @param src 原始图像
 * @param dst 旋转后的图像
 * @param degree 旋转的角度，单位度。逆时针旋转为正。
 */
void rotate(const cv::Mat &src, cv::Mat &dst, double degree);

/**
 * @brief rotate  图像旋转，不改变图像的大小。以 center 为旋转中心。
 * @param src 原始图像
 * @param dst 旋转后的图像
 * @param degree 旋转的角度，单位度。逆时针旋转为正。
 * @param center 旋转中心
 */
void rotate(const cv::Mat &src, cv::Mat &dst, double degree, cv::Point2f center);

/**
 * @brief The TemplateMatch class 这个类主要解决图像旋转匹配问题。 如果不考虑旋转那么 match 函数就够用了。
 *
 */
class TemplateMatch
{
public:
    TemplateMatch();

    /**
     * @brief setMethod 设置寻找最佳角度的方法
     * @param method
     */
    void setMethod(int method) {m_method = method;}
    /**
     * @brief setTemplate 设置模板
     * @param tem 初始模板
     * @param start_theta 起始旋转角度。逆时针旋转为正。
     * @param delta_theta 旋转角度。逆时针旋转为正。
     * @param N 共生成 N 个模板(N 为奇数)，每个模板旋转 delta_theta 角度。
     */
    void setTemplate(cv::Mat tem, double start_theta, double delta_theta, unsigned int N);

    /**
     * @brief setTemplate 设置模板, 正负对称旋转
     * @param tem
     * @param delta_theta 旋转角度。
     * @param N 共生成 2N+1 个模板，每个模板旋转 delta_theta 角度。
     */
    void setTemplate(cv::Mat tem, double delta_theta, unsigned int N);
    /**
     * @brief match
     * @param image 输入图像
     * @param center 返回模板的中心点在图像中的位置，由于图像要旋转，所以选择中心点比左上角方便。
     * @param theta  返回旋转角度。逆时针旋转为正。
     * @param method
     * @return
     */
    double match(cv::Mat image, cv::Point2f &center, double &theta, int method);
    void showTemplate();
    bool showTemplate(double theta);
private:
    double match1(const cv::Mat &image, cv::Point2f &center, double &theta, int method); //穷举
    double match2(const cv::Mat &image, cv::Point2f &center, double &theta, int method); //先正转，再反转
    double match3(const cv::Mat &image, cv::Point2f &center, double &theta, int method); //先正转，再反转
    bool better(double oldGrade, double newGrade, int method);
    std::vector<cv::Mat> m_templates;
    double m_start_theta;
    double m_delta_theta;
    int m_method;
};

#endif // TEMPLATEMATCH_H
